package com.qunar.train.mock.util;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

/**
 * Created by hongzhe.yao on 2018/8/8.
 */
public class ClassUtils {

    private static ClassUtils instance = new ClassUtils();
    // 是否循环迭代(包含子目录)
    private boolean recursive = true;

    /**
     * find in dir-mock, get all classes
     */
    public static Set<Class<?>> getClasses() {
        Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
        String packageName = instance.getMockPackage();
        String packageDirName = packageName.replace('.', '/');

        // 定义一个枚举的集合 并进行循环来处理这个目录下的things
        Enumeration<URL> dirs;
        try {
            dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
            while (dirs.hasMoreElements()) {
                URL url = dirs.nextElement();
                String protocol = url.getProtocol();
                if ("file".equals(protocol)) {
                    String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                    // 以文件的方式扫描整个包下的文件 并添加到集合中
                    instance.findAndAddClassesInPackageByFile(packageName, filePath, instance.isRecursive(), classes);
                }
            }
        } catch (IOException e) {
            AegisLog.error(e);
            //e.printStackTrace();
        }
        return classes;
    }

    private void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, Set<Class<?>> classes) {
        File dir = new File(packagePath);
        if (!dir.exists() || !dir.isDirectory()) {
            return;
        }

        // 获取包下的所有文件 包括目录
        File[] dirfiles = dir.listFiles(new FileFilter() {
            // 自定义过滤规则
            public boolean accept(File file) {
                return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
            }
        });

        if (dirfiles == null) {
            return;
        }
        // 循环所有文件
        for (File file : dirfiles) {
            if (file.isDirectory()) {
                findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, classes);
            } else {
                //java类文件去掉后面的.class 只留下类名
                String className = file.getName().substring(0, file.getName().length() - 6);
                try {
                    //classes.add(Class.forName(packageName + '.' +className));
                    classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));
                } catch (ClassNotFoundException e) {
                    AegisLog.error(e);
                    //e.printStackTrace();
                }
            }
        }
    }

    /**
     * get package data for mock-dir
     */
    private String getMockPackage() {
        File file = new File("");
        try {
            List mockDirList = new ArrayList();
            FileAndDirSearch.findDirs(file.getAbsolutePath() + "/src/test/java", "mock", mockDirList);
            if (mockDirList.isEmpty()) {
                throw new NullPointerException("mockDirList is null!");
            } else {
                String fullPath = mockDirList.get(0).toString();
                return fullPath.substring(fullPath.lastIndexOf("java") + 5).replace("\\", ".").replace("/", ".");
            }
        } catch (Exception e) {
            AegisLog.error(e);
        }
        return "";
    }

    public static void setRecursive(boolean boo) {
        instance.recursive = boo;
    }

    public static boolean isRecursive() {
        return instance.recursive;
    }

    /**
     * api mock class member-reset
     */
    public static boolean clear() {
        try {
            for (Class clazz : instance.getClasses()) {
                //Object obj = clazz.newInstance();
                //AegisLog.log(clazz.getName());
                Method method = clazz.getMethod("clear", new Class[]{});
                //method.invoke(obj, null);
                method.invoke(null, null);
            }
        } catch (Exception e) {
            AegisLog.error(e);
            return false;
        }
        return true;
    }
}
